
//
//  CMLogService.swift
//  covermedia
//
//  Created by travel on 16/3/24.
//  Copyright © 2016年 covermedia. All rights reserved.
//

import Foundation
import RealmSwift
import Async

@objc public enum CMLWhere:Int {
    case app = 0 // app
    case basic // 基本信息 {deviceType: String, networkType: String, MNO: String, versionName: String, location: String，channel: String}
    case pageFastNews // 快讯
    case pageSelected // 精选
    case pageMy // 我的
    case btnCover // TabBtn封面
    case btnDiscover // TabBtn发现
    case btnMy // TabBtn我的
    case pageNewsDetail // 新闻详情
    case pageComment // 评论列表
    case pageSubject // 专题 {subjectName: String, subjectId: Long}
    case banner // Banner
    case pageDiscover // 发现页 {channelName: String, channelId: Long}
    case pageHotSearch // 搜索（热搜）
    case pageSearch // 搜索结果页
    case pageFavor // 收藏
    case pageReadHistory // 最近阅读
    case pageFeedback // 反馈
    case pageSetting // 设置
    case pageAbout // 关于
    case pageProfile // 个人资料
    case pageLogin // 登录
    case pageRegister // 注册
    case pageFlash // 闪屏
    case pageAdvertisement // 广告跳转到的webview
    case pageSubscribe // 频道设置页面
    case pageVideoFullScreen // 视频全屏页面
    case pageLeftMenu        // 侧滑设置页
    case pageDaily        // 封面日报
    case pageMain    // 新闻主页
    case pageTopic    // 专题页面
    case pageSubChannel // 二级频道页
    case pageColumnCenter // 栏目中心
    case pageTag        //  标签页
    case pageCollectionPush // 推送集合页
    case pageDialyCustom  // 日报定制页
    case pageMySubscribe  // 我的订阅
    case pageMySubscribeTopic // 专栏页面（我的订阅）
    case pageMySubscribeCoverAccount // 封面号页面（我的订阅）
    case pageCoverAccount // 封面号页面
    case pageKnowledgeBrowser // 知识图谱页面
    case pageCAChannel  // 封面号频道
    case pageCAClass    // 封面号分类页面
    case pageMessageComment // 我的消息－评论列表
    case pageMessagePraise  // 我的消息－点赞列表
    case pageMessageNotification // 我的消息－通知列表
    case pageMessageMyMessage   // 我的消息
    case pageActivityCenter //活动中心页面
    
    //--------by gesan-----------------------
    
    case pageTabChannel           //首页频道页
    case pageTabLive              //首页直播页
    case pageTabCoverAccount      //首页封面页
    case pageTabMine              //首页我的页
}

@objc public enum CMLEvent:Int {
    case none = 0 // 无操作
    case clickAdvertisement // 点击广告
    case pageEnter // 页面进入
    case pageExit // 页面退出
    case clickSubscribe // 点击订阅
    case scrollDown // 下拉刷新 {count: Int}
    case scrollUp // 上拉刷新 {count: Int}
    case clickPlayVideo // 视频点击播放
    case clickFullScreenVideo // 视频点击全屏
    case scrollBottom // 滑动到底部
    case clickFavor // 点击收藏 {newsId: Long, favor: Bool}
    case clickSearchBtn // 点击搜索
    case clickSubmitComment // 点击评论 {newsId: Long}
    case clickShare // 点击分享 {platform: CMSocialShareType, id: (新闻为newsid，专题为subjectid), type: 0(新闻)｜1(专题)| 2(日报)}
    case clickRelatedNews // 点击相关新闻 {newsId: Long}
    case clickNewsItem // 点击一条新闻 {newsId: Long, channelId: Int, channelType: Int}
    case scroll // 滑动
    case search // 点击搜索 {keyword: String}
    case clickFontSetting // 点击字体设置 {size: CMLFontSize}
    case clickCheckVersion // 检查版本
    case clickClearCache // 点击清除缓存
    case clickPush // 点击推送通知开关 {isOpen: Bool}
    case clickTheme // 主题模式(黑夜白天) {isOpen: Bool}
    case clickEidtAvatar // 编辑头像
    case clickEditNickName // 编辑昵称
    case clickEditSex // 编辑性别
    case clickBindMobile // 绑定手机
    case clickEidtPassword // 修改密码
    case clickRegisterMobile // 手机注册
    case clickForgetPassword // 忘记密码
    case clickSmsCode // 点击获取手机验证码
    case clickUserAgreement // 点击用户协议
    case clickShowPassword // 点击显示密码
    case clickLoginThird // 点击第三方登录 {platform: CMLLoginType}
    case clickLoginOut   // 退出登录
    case clickEditAge // 编辑年龄
    case clickPraise     // 点赞 {newsId:Long, on: Bool}
    case clickSubscribeEdit // 点击订阅编辑/完成 {on:Bool}
    case clickSubcribeAdd // 点击订阅频道添加
    case clickSubcribeDel // 点击订阅频道删除
    case dragToMoveChannels // 频道拖动排序
    case enterBackgound     // app退到后台/从后台打开 ｛on:Bool｝
    case clickMore     //  点击查看更多
    case clickSubjectShare // 点击分享专题按钮{newsId: Long}
    case clickNewsDelete      // 点击删除新闻{newsId: Long}
    case scrollLeft     // 左滑退出
    case clickChannelSubscribe // 点击频道订阅
    case clickAttention  // 点击关注（专栏）{from: int(15：猜你喜欢 16：订阅中心 17：文章详情 10：专栏列表 8：画中画专栏)}
    case clickCustomMade // 点击定制封面(封面日报页面)
    case clickBeginCustomMade  // 点击开始定制
    case clickCreateCover   // 点击生成封面
    case newsReadTime   // 阅读时间{newsId: Long, time: Long(秒)}
    case clickChangeRec  // 点击换一换
    case clickGuessYouLike // 点击猜你喜欢
    case clickGuessTopic  // 点击猜你喜欢中的栏目
    case clickGuessSubcribe // 点击猜你喜欢中的栏目订阅按钮
    case clickMySubcribeInRecList // 点击推荐列表中的我的订阅
    case clickMySubcribeNewsInRecList // 点击推荐列表中的我的订阅中的新闻
    case clickMySubcribeNameInRecList  // 点击推荐列表中的我的订阅中的栏目名字
    case clickSplashAd    // 点击闪屏页广告 {id:(广告id)}
    case clickCAInSearch  // 搜索页面，封面号点击 {id（封面号id）}
    case clickCASubscribe // 封面号订阅/取消 { id（封面号id） on (是否订阅) from：int(15：猜你喜欢 16：订阅中心 17：文章详情 10：专栏列表 8：画中画专栏) }
    case clickMoreCAInChannelCA // 点击 订阅更多封面号（封面号频道）
    case clickCAClass   // 点击封面号分类 id（分类id）
    case goToDetail    // 进入新闻详情
    case clickReComment // 点击评论回复
    case clickCommentDelete // 点击评论删除
    case clickInputSwitch  // 点击表情／键盘切换按钮
    case clickCommentPraise // 点击评论点赞
    case clickEmoji  // 点击某个表情{value:String}
    case clickCommentListReplay // 点击回复按钮（评论页卡）
    case clickCommentListReplayRegion // 点击回复区域（评论页卡）
    case clickMessageToDetail // 点击详情跳转区域（评论页卡，点赞页卡）
    case clickMessageNotificationSkipRegion // 点击跳转区域（通知列表）
    case clickMessageSwitch // 我的消息，页卡切换 {value:Int(0：评论；1：点赞；2：通知)}
    case clickToChannel //频道订阅页面 点击频道进入对应的频道
    case clickToFullscreen //视频小窗点击全屏（ios）
    case clickCloseMiniVideo //视频小窗点击关闭（ios）
    case scrollInSubject //画中画 左右滑动 {from:15：猜你喜欢 3：相关新闻}
    case goToSubject //进入专栏列表页面 {from：15：猜你喜欢 7：我的订阅 17：文章详情 8：画中画专栏 19：全部专栏（订阅中心}
    case goToCoverAccount //进入封面号列表页面 ｛from：15：猜你喜欢 7：我的订阅 17：文章详情  20：封面号推荐（订阅中心）18：封面号管理页面｝
    case launchApp //进入app ｛from：21：直接启动 22：推送启动｝
    case goToDaily //进入日报 ｛from：21：直接进入 22：推送进入 23：侧边栏｝
    //--------by gesan-----------------------
    case enterPage         //页面进入
    case quitPage          //页面退出
    case newsFavor         //收藏新闻
    

    case commentAdd          //type 0 : 点击评论发送按钮 1 : 点击评论框 2 : 点击他人评论 仅当type=2的时候，有replyId；
    case userSetting         //type0 : 头像 1 : 昵称 2 : 性别 3 : 年龄 4 : 修改密码 5 : 绑定手机号 6 : 白天夜间模式 7 : 字体大小 8 : 清除缓存 9 : 推送通知10 : 版本检测 11 : 意见反馈 12 : 关于封面
    
    case channelListUpdate   //新闻列表刷新 type0 : 下拉刷新1 : 获取更多2 : 推荐列表中的“上次读到这里 点击刷新”from 0 : 主页页卡1 : 直播页卡 2 : 封面号页卡 3 ：推荐列表newsList［newsId］ 仅当from = 0的时候，才有channelI 和 channelType 仅当from = 3，才有newsList
    case newsDelete          //newsId (long)channelId (long) channelType (int)
    
    case subcribe           //关注订阅定制
    
    case searchAction            //type(0 : 推荐列表顶部搜索1 : 封面号列表顶部搜索2 : 点击搜索功能按钮 仅当type = 2，才有keyword),keyword (string), from( 0：搜索封面号+新闻 1：搜索封面号), resultType( 0：封面号 1：新闻 仅当type = 5，才有resultType)

    case listScroll          //｛type (int)，channelId (long），channelType (int)，newsId (int)，direction(int)｝type 0 : 主页页卡1 : 直播页卡 2 : 封面号页卡 3 : 猜你喜欢（deprecated） 4 : 专栏 5 : 新闻详情 6：猜你喜欢（专栏）7：猜你喜欢（封面号）8：直播详情页面的聊天室 9:封面日报 direction 0：手势向下滑动 1：手势向上滑动 当type = 0，才有channelId 和 channelType 当type = 5，才有newsId 当type = 8，9，才有direction
    
    case channelSwitch       //主页频道切换 ｛type (int)，channelId (long)，channelType (int)｝ type0 : 点击tab切换 1 : 滑动切换
    
    case useTime           //使用时长 ｛type (int)，time (int)，newsId (long｝ type 0 : app1 : 新闻详情 仅当type = 1, 才有newsIdtime单位：秒
    
    case clickAction   //点击事件 ｛type（int），preOn(bool)，preFrom(int)，liveTab(int)｝ type 0 : 点击重新选择人群item，2：点击猜你喜欢（专栏）里面的一条内容，3：点击猜你喜欢（封面号）里面的一条内容，4：封面日报页面，点击左上角返回按钮，5：人群选择页面，点击跳过按钮，6：直播Tab，点击预告提示栏，7：点击预约，8：点击分享按钮（直播），9：直播详情页面，点击回到底部按钮，10：直播详情页面，聊天室和直播厅切换, 11：猜你喜欢（专栏），点击订阅更多固定位 12：猜你喜欢（封面号），点击订阅更多固定位 13：封面号tab页面，点击“我的封面号”入口 14：封面号tab页面，点击“更多封面号”入口 15:封面推荐频道的活动入口，点击“查看更多” 16:封面推荐频道的活动入口，点击“活动图片和标题”
}

public enum CMLShareType:Int {
    case wechat = 0 // 微信
    case weibo // 微博
    case qq // QQ
    case timeLine // 朋友圈
}

public enum CMLFontSize:Int {
    case small = 0      // 小
    case middle         // 中
    case big            // 大
}

public enum CMLLoginType:Int {
    case wechat = 0     // 微信
    case weibo          // 微博
    case qq             // QQ
}

public enum CMLDetailFrom:Int {
    case ignore = -1
    case daily = 0  // 日报
    case push   // 推送
    case channelList    // 新闻列表
    case relateNews // 相关新闻
    case message    // 我的消息
    case history    // 最近阅读
    case search     // 搜索
    case mySubscribeInChannnel // 我的订阅 (列表中)
    case topicInChannnel  // 专栏（列表中的）
    case subjectList    // 专题列表
    case topicList      // 专栏列表
    case coverAccountList   // 封面号列表
    case banner     // bannner
    case favor      // 我的收藏
    case pushCollection //推送集合
    case guess       // 猜你喜欢
    case scribeCenter // 订阅中心
    case newsDetail // 文章详情
    case caManager  // 封面号管理页面
    case subjectCenter // 全部专栏（订阅中心）
    case coverAccountCenter // 封面号推荐（订阅中心）
    case normalLaunch  // 直接启动
    case pushLauch // 推送启动
    case leftMenu // 侧边栏
    case liveTab  // 直播tab
    case coverAccountTab // 封面号tab
    case liveBanner   // 直播banner位
    case coverAccountBanner // 封面号banner位
    case thousandFaces // 千面频道
    case seriesRecommend // 系列推荐
    case livePredictList  //直播预告列表
}

class CMLRecordEnityItem: Object {
    dynamic var t: Double = 0
    dynamic var w: Int = 0
    dynamic var a: Int = 0
    dynamic var o: String = ""
    dynamic var u: Int = 0
    dynamic var seq: Int = 0

    convenience init(record:CMLRecord, seq:Int) {
        self.init()
        self.seq = seq
        self.t = record.t
        self.w = record.w.rawValue
        self.a = record.a.rawValue
        self.u = record.u
        if let v = record.o?.toJsonString() {
            self.o = v
        }
    }

    override static func primaryKey() -> String? {
        return "seq"
    }

    func toDict() -> [String: Any] {
        let data:[String: Any] = ["t": ceil(t * 1000), "w": w, "a": a, "o": o, "u": u]
        return data
    }


}

class CMLRecord {
    var t: Double = 0 // when
    var w: CMLWhere = .app
    var a: CMLEvent = .none
    var o: [String: Any]?
    var u: Int = 0
    init(w:CMLWhere, e: CMLEvent, v:[String: Any]? = nil) {
        t = ceil(Date.init().timeIntervalSince1970)
        self.w = w
        self.a = e
        self.o = v
        self.u = LoginManager.shareManager.currentUserID
    }
}

open class CMRecordUseTime {
    public var time_begain : Double = 0
    public var time_end : Double = 0
    
    public init() {
    }
    
    public func now_time() -> Double {
        return ceil(Date.init().timeIntervalSince1970)
    }
    
    public func use_time() -> Double {
        return ceil(now_time() - time_begain)
    }
}

@objc public class CMLogService:NSObject {
    
    fileprivate static let shared = CMLogService()
    
    var records:[CMLRecordEnityItem] = [CMLRecordEnityItem]()

    var isLoaded:Bool? = false

    var seq: Int = 0
    
    var dbServiceQueue:DispatchQueue!
    
    var isSending:Bool = false
    
    override init() {
        dbServiceQueue = DispatchQueue(label: "com.covermedia.logPersistenceServiceQueue", attributes: []);
    }

    fileprivate func getRecordSeq() -> Int {
        if(isLoaded == true) {
            seq += 1
            return seq
        } else {
             let realm = getRealm()
             if let item = realm.objects(CMLRecordEnityItem.self).sorted(byProperty: "seq",ascending: false).first {
                seq = item.seq + 1
             } else {
                seq = 1
             }
            
             isLoaded = true
        }
        return seq
    }

    fileprivate func getRealm() -> Realm {
        return try! Realm()
    }

    fileprivate func addRcord(_ record: CMLRecordEnityItem) {
        records.append(record)
        if(records.count >= 100) {
            CMLogService.saveToDB()
            CMLogService.sendRecordsFromDB()
        }
    }
    
    fileprivate func saveRecords(_ items:[CMLRecordEnityItem]) {
        let realm = self.getRealm()
        try! realm.write(){
            realm.add(items, update:true)
        }
    }

    public class func sendRecordsFromDB() {
        if CMNetwork.isReachable {
            Async.custom(queue: CMLogService.shared.dbServiceQueue) {
                if shared.isSending {
                    return
                }
                shared.isSending = true
                let realm = CMLogService.shared.getRealm()
                let items = realm.objects(CMLRecordEnityItem.self).sorted(byProperty: "seq", ascending: true).prefix(100);
                if(items.count > 0) {
                    let lastSeq = items.last!.seq
                    CMNetDataService.shared.sendLog(Array(items), success:{
                        () -> Void in
                        Async.custom(queue: CMLogService.shared.dbServiceQueue) {
                            let realm = try! Realm()
                            let items = realm.objects(CMLRecordEnityItem.self).filter("seq <= %d", lastSeq)
                            try! realm.write(){
                                realm.delete(items)
                            }
                            shared.isSending = false
                        }
                    }, failed: { (error) in
                        Async.custom(queue: CMLogService.shared.dbServiceQueue) {
                            shared.isSending = false
                        }
                    })
                } else {
                    shared.isSending = false
                }
            }
        }
    }

    public class func log(_ w:CMLWhere, e: CMLEvent, v:[String: Any]? = nil) {
        Async.custom(queue: shared.dbServiceQueue) {
            let record = CMLRecordEnityItem(record: CMLRecord.init(w: w, e: e, v: v), seq: shared.getRecordSeq())
            shared.addRcord(record)
        }
    }

    public class func saveToDB() {
        Async.custom(queue: shared.dbServiceQueue) {
            if(shared.records.count > 0) {
                var toDBRecords = [CMLRecordEnityItem]()
                toDBRecords.append(contentsOf: CMLogService.shared.records)
                CMLogService.shared.records.removeAll()
                CMLogService.shared.saveRecords(toDBRecords)
            }
        }
    }
    
    public class func forceSendRecord() {
        CMLogService.saveToDB()
        CMLogService.sendRecordsFromDB()
    }

}
extension CMNetDataService {
    
    
func sendLog(_ records:[CMLRecordEnityItem],
             success:((Void) -> Void)? = nil,
             failed:((CMBaseError) -> Void)? = nil,
             start:((Void) -> Void)? = nil,
             finish:((Void) -> Void)? = nil) {
    if records.isEmpty {
        return
    }
    let datas = records.map({ $0.toDict() })
    request.post(
        url: .track,
        parameters: ["list": datas],
        success: { (result: CMResBase?) -> Void in
            success?()
    },
        failed: failed,
        start: start,
        finish: finish)
}

}
protocol CMLDBPersistence {
    func addRecord(_ record:CMLRecord) -> Bool
    func deleteRecords(_ records:[CMLRecord]) -> Bool
    func fetchRecords(_ size:Int) -> [CMLRecord]
    
}

class CMLDBPersistenceImpl: CMLDBPersistence {
    
    func addRecord(_ record: CMLRecord) -> Bool {
        return true
    }
    
    func deleteRecords(_ records:[CMLRecord]) -> Bool {
        return true
    }
    
    func fetchRecords(_ size: Int) -> [CMLRecord] {
        return []
    }
}

